/*
 * User: user
 * Date: 15/12/2007
 * Time: 18:37:21
 */
package org.conann.metadata.records;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.Sets;
import net.jcip.annotations.Immutable;
import org.conann.container.Creator;
import org.conann.container.InstanceCreator;

import java.lang.reflect.Method;
import java.util.*;

@Immutable
public class TypeRecord<T> extends Record<Class<T>> {

    private final Set<Class<? super T>> interfaces = new HashSet<Class<? super T>>();
    private final Map<Method, MethodRecord> methodRecords = new HashMap<Method, MethodRecord>(8);
    private final Set<Class<? super T>> apiTypes;
    private final Creator<T> creator;

    public TypeRecord(final Class<T> element) {
        super(element);
        creator = new InstanceCreator<T>(element);
        apiTypes = resolveApiTypes();
    }

    public MethodRecord getRecord(Method method) {
        checkNotNull(method);
        return checkNotNull(methodRecords.get(method));
    }

    public void addRecord(MethodRecord record) {
        checkNotNull(record);
        methodRecords.put(record.getMethod(), record);
    }

    public boolean isImplementing(Class<? super T> ifc) {
        checkNotNull(ifc);
        return interfaces.contains(ifc);
    }

    public void addInterface(Class<? super T> ifc) {
        checkNotNull(ifc);
        checkArgument(ifc.isAssignableFrom(getType()));
        interfaces.add(ifc);
    }

    public Creator<T> getCreator() {
        return creator;
    }

    public Class<T> getType() {
        return getElement();
    }

    public Set<Class<? super T>> getApiTypes() {
        return apiTypes;
    }

    private Set<Class<? super T>> resolveApiTypes() {
        Set<Class<?>> apiTypes = Sets.newHashSetWithExpectedSize(8);
        Class<? super T> typeIterator = getType();
        do {
            apiTypes.add(typeIterator);
            apiTypes.addAll(Arrays.asList(typeIterator.getInterfaces()));
            typeIterator = typeIterator.getSuperclass();
        } while (typeIterator != null);
        // generics drove me mad:
        Set<Class<? super T>> result = Sets.newHashSetWithExpectedSize(apiTypes.size());
        for (Class<?> apiType : apiTypes) {
            result.add((Class<? super T>) apiType);
        }
        return Collections.unmodifiableSet(result);
    }

    @Override
    public String toString() {
        return "TypeRecord [" + getElement().getSimpleName() + ']';
    }
}